package pipe

import (
	"gitee.com/injoyai/goutil/chans"
	"gitee.com/injoyai/goutil/conv"
	"gitee.com/injoyai/goutil/g"
	"gitee.com/injoyai/goutil/maps"
	"gitee.com/injoyai/goutil/net/conn/v2"
	"gitee.com/injoyai/goutil/net/tcp/v2"
	"gitee.com/injoyai/goutil/oss/io"
	"log"
	"strings"
)

func NewClient(addr, sn string, num ...int) *Client {
	cli := &Client{
		sn:    sn,
		addr:  addr,
		queue: chans.NewEntity(conv.GetDefaultInt(20, num...)),
		Proxy: newProxy(sn),
	}
	cli.queue.SetHandler(cli.dealQueue)
	cli.Client = cli.newClient(addr, sn, -1)
	cli.Proxy.setDealFunc(func(key string, msg *conn.Message) {
		cli.Client.Write(newWriteMsg(key, addr, msg.String()).Bytes())
	})
	cli.Proxy.setCloseFunc(func(key, msg string) {
		cli.Client.Write(newCloseMsg(key, msg).Bytes())
	})
	return cli
}

type Client struct {
	sn          string        //客户端标识
	addr        string        //通道服务端地址
	*tcp.Client               //通道客户端
	queue       *chans.Entity //执行队列
	Proxy       *Proxy        //代理客户端连接池
}

// Write 给服务端发数据
func (this *Client) Write(msg []byte) (int, error) {
	return this.Client.Write(newInfoMsg(this.sn, msg).Bytes())
}

// SetRedirectFunc 自定义重定向函数
func (this *Client) SetRedirectFunc(fn func(string) string) *Client {
	this.Proxy.setRedirectFunc(fn)
	return this
}

// Redirect 重定向,*表示重定向全部,用默认重定向函数是生效
func (this *Client) Redirect(oldAddr, newAddr string) *Client {
	this.Proxy.setRedirect(oldAddr, newAddr)
	return this
}

// Wait 一直等待
func (this *Client) Wait() {
	select {}
}

// dealQueue 队列,协程控制
func (this *Client) dealQueue(no, num int, data interface{}) {
	msg := data.(*conn.Message)
	m, err := decodeMsg(msg.Bytes())
	if err != nil {
		return
	}
	if err := this.Proxy.do(m); err != nil {
		log.Printf("[错误][%s] %s", msg.Key(), err.Error())
		this.Client.Write(m.Close(err.Error()).Bytes())
	}
}

// newClient 新建通道客户端
func (this *Client) newClient(addr, sn string, num int) *tcp.Client {
	return tcp.Redial(addr, func(c *tcp.Client) {
		this.Proxy.closeAll()
		c.SetPrintFunc(PrintWithClient)
		c.SetWriteFunc(WriteWithBase64)
		c.SetReadFunc(ReadWithBase64)
		c.SetDealFunc(func(msg *io.Message) { this.queue.Do(msg) })
		c.SetPrintWithASCII()
		c.SetKey(sn)
		c.Write(newRegisterMsg(sn, conv.String(num)).Bytes())
		c.SetKeepAlive(DefaultKeepAlive, newRegisterMsg(sn, conv.String(num)).Bytes())
	})
}

//==================================Proxy==================================

func newProxy(sn string) *Proxy {
	p := &Proxy{
		sn:        sn,
		conn:      maps.NewSafe(),
		closeFunc: nil,
		dealFunc:  nil,
		redirect:  maps.NewSafe(),
	}
	p.redirectFunc = p.getAddr
	return p
}

type Proxy struct {
	sn           string                              //
	conn         *maps.Safe                          //代理的客户端连接
	closeFunc    func(key, msg string)               //关闭事件函数
	dealFunc     func(key string, msg *conn.Message) //处理数据函数
	redirect     *maps.Safe                          //重定向
	redirectFunc func(string) string                 //自定义重定向
	debug        bool
}

func (this *Proxy) Debug(b ...bool) {
	this.debug = !(len(b) > 0 && !b[0])
}

// getAddr 获取重定向之后的地址
func (this *Proxy) getAddr(addr string) string {
	x := this.redirect.GetVar("*").String(addr)
	return this.redirect.GetVar(addr).String(x)
}

// setRedirect 重定向,*表示重定向全部,用默认重定向函数是生效
func (this *Proxy) setRedirect(oldAddr, newAddr string) *Proxy {
	if len(newAddr) > 0 {
		if !strings.Contains(newAddr, ":") {
			newAddr += ":80"
		}
		this.redirect.Set(oldAddr, newAddr)
	}
	return this
}

func (this *Proxy) setRedirectFunc(fn func(string) string) *Proxy {
	this.redirectFunc = fn
	return this
}

func (this *Proxy) setCloseFunc(fn func(key, msg string)) *Proxy {
	this.closeFunc = fn
	return this
}

func (this *Proxy) setDealFunc(fn func(key string, msg *conn.Message)) *Proxy {
	this.dealFunc = fn
	return this
}

func (this *Proxy) do(m *Message) (err error) {
	defer g.Recover(&err)
	if this.redirectFunc != nil {
		m.Addr = this.redirectFunc(m.Addr)
	}
	switch m.Type {
	case Connect:
		_, err = this.connect(m.Key, m.Addr)
	case Write:
		err = this.write(m.Key, m.Addr, m.Data)
	case Close:
		this.close(m.Key)
	}
	return
}

func (this *Proxy) connect(key string, addr string) (*tcp.Client, error) {
	i := this.conn.GetInterface(key)
	if i == nil || i.(*tcp.Client).Err() != nil {
		c, err := tcp.Dial(addr)
		if err == nil {
			log.Printf("[连接][pipe][%s] 连接服务成功...", addr)
			c.SetPrintFunc(func(s string, msg *io.Message) {
				log.Printf("[%s][pipe][%s] %s\n", s, msg.Key(), msg.String())
			})
			c.SetTimeout(DefaultKeepAlive)
			c.SetKey(key)
			c.SetPrintWithASCII()
			c.Debug(this.debug)
			c.SetWriteFunc(nil)
			c.SetReadFunc(io.ReadWithAll)
			c.SetCloseFunc(func(msg *conn.Message) {
				if this.closeFunc != nil {
					this.closeFunc(key, msg.String())
				}
				log.Printf("[信息][%s][%s] %s", this.sn, addr, msg.String())
				this.conn.Del(key)
			})
			c.SetDealFunc(func(msg *conn.Message) {
				if this.dealFunc != nil {
					this.dealFunc(key, msg)
				}
			})
			this.conn.Set(key, c)
		}
		return c, err
	}
	return i.(*tcp.Client), nil
}

func (this *Proxy) write(key string, addr, msg string) error {
	c, err := this.connect(key, addr)
	if err != nil {
		return err
	}
	return c.WriteString(msg)
}

func (this *Proxy) close(key string) {
	i := this.conn.GetInterface(key)
	if i != nil {
		i.(*tcp.Client).Close()
		this.conn.Del(key)
	}
}

func (this *Proxy) closeAll() {
	this.conn.Range(func(key, value interface{}) bool {
		_ = value.(*tcp.Client).Close()
		return true
	})
	this.conn = maps.NewSafe()
}
